unit ORM.Base;

interface

uses
  DBXJSON, DBXJSONReflect, DBXCommon, RTTI, TypInfo, ORM.Attributes, SysUtils,   DBClient,
  Classes,DB, FMTBcd, SqlTimSt, jpeg;

type
  TJSonVO = class
  public
    constructor Create; overload; virtual;
    constructor Create(pJsonValue: TJSONValue); overload; virtual;
    function Clone: TJSonVO;
    function ToJSON: TJSONValue; virtual;
    function ToJSONString: string;

    class function ObjectToJSON<O : class>(objeto: O): TJSONValue;
    class function JSONToObject<O : class>(json: TJSONValue): O;
  end;

  TClassJson = class of TJSonVO;

  TGeneric<T: class> = class
  public
    class function GetColumn(pName: string): TColumn;
    class function CreateObject: T;
    class function FromClienteDataSet(pClienteDataSet: TClientDataSet): T;
    class function FieldCaption(pFieldName: string): string;
    class function FieldLength(pFieldName: string): Integer;
  end;

  function VOFromDBXReader(pObj: TJSonVO; pReader: TDBXReader): TJSonVO;

implementation

uses
  System.StrUtils;

{ TJSonVO }

constructor TJSonVO.Create;
begin
  inherited Create;
end;

constructor TJSonVO.Create(pJsonValue: TJSONValue);
var
  Deserializa: TJSONUnMarshal;
begin
  if pJsonValue is TJSONNull then
    Exception.Create('Erro ao criar objeto com um valor nulo!');

  Deserializa := TJSONUnMarshal.Create;
  try
    Self.Free;
    Self := Deserializa.Unmarshal(pJsonValue) as TJSonVO;
  finally
    Deserializa.Free;
  end;
end;

function TJSonVO.Clone: TJSonVO;
var
  Contexto: TRttiContext;
  Tipo: TRttiType;
  Field: TRttiField;
begin
  Result :=  Self.NewInstance as TJSonVO;
  Contexto := TRttiContext.Create;
  try
    Tipo := Contexto.GetType(Self.ClassType);
    for Field in Tipo.GetFields do
      Tipo.GetField(Field.Name).SetValue(Result,Field.GetValue(Self));
  finally
    Contexto.Free;
  end;
end;

function TJSonVO.ToJSON: TJSONValue;
var
  Serializa: TJSONMarshal;
begin
  Serializa := TJSONMarshal.Create(TJSONConverter.Create);
  try
    Exit(serializa.Marshal(Self));
  finally
    Serializa.Free;
  end;
end;

function TJSonVO.ToJSONString: string;
var
  jValue: TJSONValue;
begin
  jValue := ToJSON;
  try
    Result := jValue.ToString;
  finally
    jValue.Free;
  end;
end;

class function TJSonVO.JSONToObject<O>(json: TJSONValue): O;
var
  deserializa: TJSONUnMarshal;
begin
  if json is TJSONNull then
    exit(nil);

  deserializa := TJSONUnMarshal.Create;
  try
    exit(O(deserializa.Unmarshal(json)))
  finally
    deserializa.Free;
  end;
end;

class function TJSonVO.ObjectToJSON<O>(objeto: O): TJSONValue;
var
  serializa: TJSONMarshal;
begin
  if Assigned(objeto) then
  begin
    serializa := TJSONMarshal.Create(TJSONConverter.Create);
    try
      exit(serializa.Marshal(objeto));
    finally
      serializa.Free;
    end;
  end
  else
    exit(TJSONNull.Create);
end;

{ TGenericVO<T> }

class function TGeneric<T>.FromClienteDataSet(pClienteDataSet: TClientDataSet): T;
var
  Obj: T;
  Contexto: TRttiContext;
  Tipo: TRttiType;
  Propriedade: TRttiProperty;
  Propriedades: TArray<TRttiProperty>;
  Atributo: TCustomAttribute;
  Value: TValue;
  I, A: Integer;
  NomeCampo: string;
  EncontrouPropriedade: Boolean;
begin
  Obj := TGeneric<T>.CreateObject;
  Contexto := TRttiContext.Create;
  try
    Tipo := Contexto.GetType(TObject(Obj).ClassType);
    Propriedades := Tipo.GetProperties;

    for I := 0 to pClienteDataSet.FieldCount - 1 do
    begin
      NomeCampo := UpperCase(pClienteDataSet.Fields[i].FieldName);
      if (pClienteDataSet.Fields[i].IsNull) or (pClienteDataSet.Fields[I].AsString = '') then
        Continue;
      case pClienteDataSet.Fields[i].DataType of
        ftString,ftWideString,ftWideMemo:
          Value := pClienteDataSet.Fields[i].AsString;
        ftInteger,ftSmallint, ftBytes,ftWord :
          Value := pClienteDataSet.Fields[i].AsInteger;
        ftTimeStamp, ftTime, ftDateTime, ftDate :
          Value := pClienteDataSet.Fields[i].AsDateTime;
        ftFloat, ftBCD, ftFMTBcd :
          Value := pClienteDataSet.Fields[i].AsFloat;
        ftBoolean :
          Value := pClienteDataSet.Fields[i].AsBoolean;

      end;

      EncontrouPropriedade := False;
      for A := 0 to Length(Propriedades) - 1 do
      begin
        Propriedade := Propriedades[A];
        for Atributo in Propriedade.GetAttributes do
        begin
          if Atributo is TColumn then
          begin
            if (Atributo as TColumn).Name = NomeCampo then
            begin
              if not Value.IsEmpty then
              begin
                Propriedade.SetValue(TObject(Obj), Value);
              end;

              EncontrouPropriedade := True;
              Break;
            end;
          end
          else
          if Atributo is TPrimaryKey then
          begin
            if (Atributo as TPrimaryKey).Name = NomeCampo then
            begin
              if not Value.IsEmpty then
              begin
                Propriedade.SetValue(TObject(Obj),Value);
              end;

              EncontrouPropriedade := True;
              Break;
            end;
          end;
        end;
        if EncontrouPropriedade then
          Break;
      end;
    end;
  finally
    Contexto.Free;
  end;
  Result := Obj;
end;

class function TGeneric<T>.GetColumn(pName: string): TColumn;
var
  Obj: T;
  Contexto: TRttiContext;
  Tipo: TRttiType;
  Propriedade: TRttiProperty;
  Atributo: TCustomAttribute;
  Encontrou: Boolean;
begin
  Result := nil;
  Obj := CreateObject;
  Contexto := TRttiContext.Create;
  try
    Tipo := Contexto.GetType(TObject(Obj).ClassType);

    Encontrou := False;
    for Propriedade in Tipo.GetProperties do
    begin
      for Atributo in Propriedade.GetAttributes do
      begin
        if Atributo is TColumn then
        begin
          if (Atributo as TColumn).Name = pName then
          begin
            Result := (Atributo as TColumn).Clone;
            Encontrou := True;
            Break;
          end;
        end;
      end;
      if Encontrou then
        Break;
    end;
  finally
    TObject(Obj).Free;
    Contexto.Free;
  end;
end;

class function TGeneric<T>.CreateObject: T;
var
  Contexto: TRttiContext;
  Tipo: TRttiType;
  Value: TValue;
  Obj: TObject;
begin
  Contexto := TRttiContext.Create;
  try
    Tipo := Contexto.GetType(TClass(T));
    Value := Tipo.GetMethod('Create').Invoke(Tipo.AsInstance.MetaclassType,[]);
    Result := T(Value.AsObject);
  finally
    Contexto.Free;
  end;
end;

class function TGeneric<T>.FieldCaption(pFieldName: string): string;
var
  Atributo: TColumn;
begin
  Atributo := GetColumn(pFieldName);

  if Assigned(Atributo) then
  begin
    Result := Atributo.Caption;
    Atributo.Free;
  end
  else
    Result := '';
end;

class function TGeneric<T>.FieldLength(pFieldName: string): Integer;
var
  Atributo: TColumn;
begin
  Atributo := GetColumn(pFieldName);
  if Assigned(Atributo) then
  begin
    Result := Atributo.Length;
    Atributo.Free;
  end
  else
    Result := 0;
end;

function VOFromDBXReader(pObj: TJSonVO; pReader: TDBXReader): TJSonVO;
var
  Contexto: TRttiContext;
  Tipo: TRttiType;
  Propriedade: TRttiProperty;
  Propriedades: TArray<TRttiProperty>;
  Atributo: TCustomAttribute;
  Value: TValue;
  I, A: Integer;
  NomeCampo: string;
  DBXValueType: TDBXValueType;
  DBXValue: TDBXValue;
  EncontrouPropriedade: Boolean;
begin
  Result := pObj;
  Contexto := TRttiContext.Create;
  try
    Tipo := Contexto.GetType(Result.ClassType);
    Propriedades := Tipo.GetProperties;

    for I := 0 to pReader.ColumnCount - 1 do
    begin
      DBXValueType := pReader.ValueType[I];
      DBXValue := pReader.Value[I];
      NomeCampo := DBXValueType.Name;

      with TDBXDataTypes do
      begin
        case DBXValueType.DataType of
          AnsiStringType, WideStringType, BlobType: Value := DBXValue.AsString;
          DateType:
          begin
            if DBXValue.AsDate > 0 then
              Value := DBXValue.AsDateTime
            else
              Value := TValue.Empty;
          end;
          DateTimeType: Value := DBXValue.AsDateTime;
          TimeType, TimeStampType: Value := DBXValue.AsTime;
          Int32Type: Value := DBXValue.AsInt32;
          Int64Type: Value := DBXValue.AsInt64;
          DoubleType, BcdType, CurrencyType: Value := DBXValue.AsDouble;
          BinaryBlobType, BytesType, VariantType: Value := TValue.FromVariant(DBXValue.AsVariant);
          BooleanType: Value := DBXValue.AsBoolean;
        else
          Value := TValue.Empty;
        end;
      end;

      EncontrouPropriedade := False;
      for A := 0 to Length(Propriedades) - 1 do
      begin
        Propriedade := Propriedades[A];
        for Atributo in Propriedade.GetAttributes do
        begin
          if Atributo is TColumn then
          begin
            if (Atributo as TColumn).Name = NomeCampo then
            begin
              if not Value.IsEmpty then
                Propriedade.SetValue(Result,Value);

              EncontrouPropriedade := True;
              Break;
            end;
          end
          else
          if Atributo is TPrimaryKey then
          begin
            if (Atributo as TPrimaryKey).Name = NomeCampo then
            begin
              if not Value.IsEmpty then
                Propriedade.SetValue(Result,Value);

              EncontrouPropriedade := True;
              Break;
            end;
          end;
        end;
        if EncontrouPropriedade then
          Break;
      end;
    end;
  finally
    Contexto.Free;
  end;
end;

end.
